#include "pch.h"

DBWrapper::DBWrapper(const Config &config) : config_(config)
#ifdef _WIN32
                                             ,
                                             conn_(nullptr)
#else
                                             ,
                                             conn_(nullptr)
#endif
{
}

DBWrapper::~DBWrapper()
{
#ifdef _WIN32
    if (conn_)
    {
        conn_->close();
    }
#else
    if (conn_)
    {
        mysql_close(conn_);
    }
#endif
}

bool DBWrapper::connect()
{
#ifdef _WIN32
    try
    {
        sql::mysql::MySQL_Driver *driver = sql::mysql::get_mysql_driver_instance();
        conn_.reset(driver->connect(config_.host, config_.user, config_.password));
        conn_->setSchema(config_.database);
        return true;
    }
    catch (const sql::SQLException &e)
    {
        std::cerr << "MySQL Connection error: " << e.what() << std::endl;
        return false;
    }
#else
    conn_ = mysql_init(nullptr);
    if (!conn_)
    {
        std::cerr << "Failed to initialize MariaDB connection" << std::endl;
        return false;
    }

    if (!mysql_real_connect(conn_, config_.host.c_str(), config_.user.c_str(),
                            config_.password.c_str(), config_.database.c_str(),
                            config_.port, nullptr, 0))
    {
        std::cerr << "MariaDB Connection error: " << mysql_error(conn_) << std::endl;
        return false;
    }

    if (mysql_set_character_set(conn_, "utf8mb4"))
    {
        std::cerr << "Failed to set character set: " << mysql_error(conn_) << std::endl;
    }

    return true;
#endif
}

bool DBWrapper::execute(const std::string &query)
{
#ifdef _WIN32
    try
    {
        std::unique_ptr<sql::Statement> stmt(conn_->createStatement());
        stmt->execute(query);
        return true;
    }
    catch (const sql::SQLException &e)
    {
        std::cerr << "MySQL Query error: " << e.what() << std::endl;
        return false;
    }
#else
    if (!conn_)
        return false;

    if (mysql_query(conn_, query.c_str()))
    {
        std::cerr << "MariaDB Query error: " << mysql_error(conn_) << std::endl;
        return false;
    }
    return true;
#endif
}

bool DBWrapper::prepareAndExecute(const std::string &query,
                                  const std::vector<std::string> &params)
{
#ifdef _WIN32
    try
    {
        std::unique_ptr<sql::PreparedStatement> pstmt(conn_->prepareStatement(query));
        for (size_t i = 0; i < params.size(); ++i)
        {
            pstmt->setString((int32_t)(i + 1), params[i]);
        }
        pstmt->execute();
        return true;
    }
    catch (const sql::SQLException &e)
    {
        std::cerr << "MySQL PreparedStatement error: " << e.what() << std::endl;
        return false;
    }
#else
    if (!conn_)
        return false;

    MYSQL_STMT *stmt = mysql_stmt_init(conn_);
    if (!stmt)
    {
        std::cerr << "Failed to initialize statement" << std::endl;
        return false;
    }

    if (mysql_stmt_prepare(stmt, query.c_str(), query.length()))
    {
        std::cerr << "Prepare error: " << mysql_stmt_error(stmt) << std::endl;
        mysql_stmt_close(stmt);
        return false;
    }

    if (mysql_stmt_param_count(stmt) != params.size())
    {
        std::cerr << "Parameter count mismatch" << std::endl;
        mysql_stmt_close(stmt);
        return false;
    }

    std::vector<MYSQL_BIND> bindings(params.size());
    std::vector<unsigned long> str_lengths(params.size());
    memset(bindings.data(), 0, sizeof(MYSQL_BIND) * bindings.size());

    for (size_t i = 0; i < params.size(); ++i)
    {
        bindings[i].buffer_type = MYSQL_TYPE_STRING;
        bindings[i].buffer = const_cast<char *>(params[i].c_str());
        bindings[i].buffer_length = params[i].length();
        str_lengths[i] = params[i].length();
        bindings[i].length = &str_lengths[i];
        bindings[i].is_null = 0;
    }

    if (mysql_stmt_bind_param(stmt, bindings.data()))
    {
        std::cerr << "Bind error: " << mysql_stmt_error(stmt) << std::endl;
        mysql_stmt_close(stmt);
        return false;
    }

    if (mysql_stmt_execute(stmt))
    {
        std::cerr << "Execute error: " << mysql_stmt_error(stmt) << std::endl;
        mysql_stmt_close(stmt);
        return false;
    }

    mysql_stmt_close(stmt);
    return true;
#endif
}

bool DBWrapper::isConnected() const
{
#ifdef _WIN32
    return conn_ && !conn_->isClosed();
#else
    return conn_ != nullptr;
#endif
}

#ifdef _WIN32
std::unique_ptr<sql::Connection> DBWrapper::getNativeConnection()
{
    return std::move(conn_);
}
#else
MYSQL *DBWrapper::getNativeConnection()
{
    return conn_;
}
#endif